package algorithms;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import framework.State;

public class ValueIteration {
	private static final double epsilon = .1, gamma = .9;
	private static double delta = .5, newValue;
	private static double errorP = .1;
	private static Map<String, String> policy = new HashMap<String, String>();

	public static void iterate() {
		while (delta > epsilon) {
			delta = 0.;
			for (double[] s : State.allStates.values()) {
				newValue = getMaxActionUtility(s);
				//
				if (Math.abs(newValue - s[4]) > delta) {
					delta = Math.abs(newValue - s[4]);
				}
				s[4] = newValue;
			}
		}
	}

	private static double getMaxActionUtility(double[] s) {
		double maxVal = -1000., current = .0;
		double[] maxState = s;
		int move;
		List<String> states = State.getPossibleTransitions(s);
		if (states.size() == 0) {
			policy.put(State.toString(s), State.toString(s));
			return State.getValue(s);
		}
		//
		for (String state : states) {
			double[] newState = State.allStates.get(state);
			if (newState[0] != s[0]) {
				move = 0; // A Moved
			} else {
				move = 1; // B Moved
			}
			current = State.getValue(newState) + (gamma * sumStates(states, newState, move));
			if (current > maxVal) {
				maxVal = current;
				maxState = newState;
			}
		}
		// Update policy
		policy.put(State.toString(s), State.toString(maxState));
		return maxVal;
	}

	private static double sumStates(List<String> states, double[] currentState, int move) {
		double sum = 0., prob = 0;
		for (String state : states) {
			double[] newState = State.allStates.get(state);
			if (newState[move] == currentState[move]) {
				continue; // Not the move we are looking for
			}
			if (currentState.equals(newState)) {
				prob = 1. - errorP;
			} else {
				prob = errorP;
			}

			sum += prob * newState[4];
		}
		return sum;
	}

	/**
	 * 
	 * @return
	 */
	public static String printPolicy() {
		StringBuilder builder = new StringBuilder();

		builder.append("----Policy-----\n");
		for (double[] s : State.allStates.values()) {
			// builder.append(State.toString(s));
			builder.append(State.toVisualString(s, true));
			builder.append("-->\n");
			// builder.append(policy.get(State.toString(s)));
			builder.append(State.toVisualString(State.allStates.get(policy.get(State.toString(s))),
					true));
			builder.append("########\n");
		}
		return builder.toString();
	}

}
